package jmine.tec.hydra.scheduling;

import jmine.tec.hydra.HydraController;
import jmine.tec.hydra.HydraMessages;
import jmine.tec.hydra.api.HydraRequest;
import jmine.tec.hydra.api.HydraSpecification;
import jmine.tec.hydra.dao.HydraSpecificationDAO;
import jmine.tec.hydra.dao.HydraSystemDAO;
import jmine.tec.hydra.domain.HydraSystem;
import jmine.tec.hydra.exception.HydraException;
import jmine.tec.scheduling.Action;
import jmine.tec.scheduling.ScheduledJobExecutionContext;
import bancosys.tec.persist.dao.BeanNotFoundException;
import bancosys.tec.utils.date.Clock;
import bancosys.tec.utils.date.Date;

/**
 * Action que executa uma {@link HydraSpecification}.
 * 
 * @author renan.kuroki
 */
public class HydraSpecificationScheduledAction implements Action {

    private HydraController hydraController;

    private HydraSpecificationDAO hydraSpecificationDAO;

    private HydraSystemDAO hydraSystemDAO;

    private Clock clock;

    private String hydraSystem;

    private String hydraAction;

    /**
     * {@inheritDoc}
     */
    public void execute(ScheduledJobExecutionContext context) throws Exception {
        HydraSystem hs = this.findHydraSystem(context);
        HydraSpecification hydraSpec = this.findHydraSpec(context, hs);
        try {
            this.sendHydra(context, hydraSpec);
        } catch (HydraException e) {
            this.addHydraLogException(context, e);
            throw e;
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * Envia integração com a data corrente, retornando uma {@link HydraRequest}.
     * 
     * @param context {@link ScheduledJobExecutionContext}
     * @param hydraSpec {@link HydraSpecification}
     * @return {@link HydraRequest}
     */
    protected HydraRequest sendHydra(ScheduledJobExecutionContext context, HydraSpecification hydraSpec) {
        HydraRequest request;
        Date currentDate = this.clock.currentDate();
        context.log(HydraMessages.SENDING_INTEGRATION_FROM_SPECIFICATION.create(hydraSpec.toString(), currentDate.toString()));
        request = this.hydraController.send(currentDate, hydraSpec);
        context.log(HydraMessages.SPECIFICATION_SENT_SUCESS.create());
        return request;
    }

    /**
     * Busca a {@link HydraSpecification} a partir do {@link HydraSystem} e HydraAction.
     * 
     * @param context {@link ScheduledJobExecutionContext}
     * @param hs {@link HydraSystem}
     * @return {@link HydraSpecification}
     * @throws BeanNotFoundException caso não encontre o {@link HydraSpecification} passado.
     */
    protected HydraSpecification findHydraSpec(ScheduledJobExecutionContext context, HydraSystem hs) throws BeanNotFoundException {
        try {
            HydraSpecification hydraSpec = this.hydraSpecificationDAO.findByActionSystem(hs, this.hydraAction);
            return hydraSpec;
        } catch (BeanNotFoundException e) {
            context.log(HydraMessages.SPECIFICATION_NOT_FOUND_SYSTEM_ACTION.create(this.hydraSystem, this.hydraAction));
            throw e;
        }
    }

    /**
     * Busca o {@link HydraSystem} pela NaturalKey.
     * 
     * @param context {@link ScheduledJobExecutionContext}
     * @return {@link HydraSystem}
     * @throws BeanNotFoundException caso não encontre o HydraSystem passado.
     */
    protected HydraSystem findHydraSystem(ScheduledJobExecutionContext context) throws BeanNotFoundException {
        try {
            HydraSystem hs = this.hydraSystemDAO.findByNaturalKey(this.hydraSystem);
            return hs;
        } catch (BeanNotFoundException e) {
            context.log(HydraMessages.SYSTEM_NOT_FOUND.create(this.hydraSystem));
            throw e;
        }
    }

    /**
     * Adiciona mensagem da exceção ocorrida no envio da requisição aos logs ao {@link ScheduledJobExecutionContext}.
     * 
     * @param context {@link ScheduledJobExecutionContext}
     * @param e Exception
     */
    protected void addHydraLogException(ScheduledJobExecutionContext context, HydraException e) {
        context.log(e.getLocalizedMessageHolder());
    }

    /**
     * @param hydraController the hydraController to set
     */
    public void setHydraController(HydraController hydraController) {
        this.hydraController = hydraController;
    }

    /**
     * @return the hydraController
     */
    public HydraController getHydraController() {
        return this.hydraController;
    }

    /**
     * @param hydraSpecificationDAO the hydraSpecificationDAO to set
     */
    public void setHydraSpecificationDAO(HydraSpecificationDAO hydraSpecificationDAO) {
        this.hydraSpecificationDAO = hydraSpecificationDAO;
    }

    /**
     * @param hydraSystemDAO the hydraSystemDAO to set
     */
    public void setHydraSystemDAO(HydraSystemDAO hydraSystemDAO) {
        this.hydraSystemDAO = hydraSystemDAO;
    }

    /**
     * @param clock the clock to set
     */
    public void setClock(Clock clock) {
        this.clock = clock;
    }

    /**
     * @return the clock
     */
    public Clock getClock() {
        return this.clock;
    }

    /**
     * @param hydraSystem the hydraSystem to set
     */
    public void setHydraSystem(String hydraSystem) {
        this.hydraSystem = hydraSystem;
    }

    /**
     * @param hydraAction the hydraAction to set
     */
    public void setHydraAction(String hydraAction) {
        this.hydraAction = hydraAction;
    }
}
